package at.fhooe.pandora.entities;

import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;

import at.fhooe.engine.entity.Entity;
import at.fhooe.engine.event.events.*;
import at.fhooe.engine.input.ControlType;
import at.fhooe.engine.mapobject.MapObject;
import at.fhooe.engine.mapobject.Wall;
import at.fhooe.engine.pandora.Game;
import at.fhooe.engine.pathfinding.Steering;
import at.fhooe.engine.pathfinding.SteeringStrategy;
import at.fhooe.engine.physics.CircleBody;
import at.fhooe.engine.physics.PhysicBody;
import at.fhooe.engine.physics.RectangleBody;
import at.fhooe.engine.physics.forces.DampingForce;
import at.fhooe.engine.physics.forces.DirectionAngularForce;
import at.fhooe.engine.physics.forces.Force;
import at.fhooe.engine.physics.forces.RoationForce;
import at.fhooe.engine.scenegraph.SceneNode;
import at.fhooe.pandora.utils.ResourceCounter;
import at.fhooe.pandora.visuals.PathVisual;

public class ShipEntity extends Entity implements KeyUpEvent.Listener, KeyDownEvent.Listener, CollisionEvent.Listener,
													MouseButtonUpEvent.Listener, MouseButtonDownEvent.Listener, 
													ControlChangedEvent.Listener, MouseMovedEvent.Listener {
	
	public static final String ID = "ShipEntity";
	public static final double BULLET_COOL_DOWN = 0.3;
	
	private static double SHIP_ACCELERATION = 360; //initial value 450
	private static double SHIP_ANGULAR_ACC = 8; // initial value 10
	
	private double m_bulletCoolDownTime;
	private boolean m_shooting;
	private boolean m_collecting;
	private PhysicBody m_physicBody;
	private CircleBody m_collectingCircle;
	private Point2D m_collectingCircleRelativePosition;
	private Steering m_Steering;
	private SteeringStrategy m_SteerStrategy;
	private ArrayList<Point> m_temporaryWayPoints;
	private int m_lifeCount;
	private double m_size;
	private int m_radius;
	
	private ResourceCounter m_resources;
	
	//TODO invert direction in class so no confusions occur
//	private Force m_ForceUP = new DirectionForce(0, -1, SHIP_ACCELERATION);
//	private Force m_ForceDOWN = new DirectionForce(0, 1, SHIP_ACCELERATION);
//	private Force m_ForceLEFT = new DirectionForce(-1, 0, SHIP_ACCELERATION);
//	private Force m_ForceRIGHT = new DirectionForce(1, 0, SHIP_ACCELERATION);
	private Force m_ForceRotLeft = new RoationForce(-1, SHIP_ANGULAR_ACC);
	private Force m_ForceRotRight = new RoationForce(1, SHIP_ANGULAR_ACC);
	private Force m_ForceInAngularDirectenForward = new DirectionAngularForce(SHIP_ACCELERATION);
	private Force m_ForceInAngularDirectenBackward = new DirectionAngularForce(-SHIP_ACCELERATION);
	
	private ControlType m_controlType = ControlType.AlternativeMouse;
	private boolean m_startedWay;
	private ArrayList<Point2D> m_mousePoints;
	private SceneNode path;
	

	public ShipEntity(String name, String timerName) {
		super(ID, name);
		m_size = 25;
		m_radius = 100;
		m_physicBody = new CircleBody(m_name, m_size, true);	//TODO check size of ship
		m_physicBody.setTimer(timerName);
		m_physicBody.addForce(new DampingForce(0.7, 0.7, 0.9)); // initial value 0.9, 0.9, 2
		m_Steering = new Steering(this);
		m_SteerStrategy = m_Steering.getStrategy();
		m_collectingCircle = new CircleBody("collectingRadius", m_radius, true);
		m_collectingCircleRelativePosition = new Point2D.Double(0, -m_radius/2);
		m_bulletCoolDownTime = -BULLET_COOL_DOWN;
		m_shooting = false;
		m_collecting = false;
		
		m_startedWay = false;
		
		m_resources = new ResourceCounter();
		
		m_lifeCount = 3;
		m_mousePoints = new ArrayList<Point2D>();
		path = Game.getInstance().getSceneNodeManager().createSceneNode("PathWay");
	}
	
	@Override
	public void initialize() {
		Game.getInstance().getPhysicsManager().addPhysicBody(m_physicBody);
		Game.getInstance().getEventManager().addListener(this, KeyUpEvent.TYPE);
		Game.getInstance().getEventManager().addListener(this, KeyDownEvent.TYPE);
		Game.getInstance().getEventManager().addListener(this, CollisionEvent.TYPE);
		Game.getInstance().getEventManager().addListener(this, MouseButtonUpEvent.TYPE);
		Game.getInstance().getEventManager().addListener(this, MouseButtonDownEvent.TYPE);
		Game.getInstance().getEventManager().addListener(this, ControlChangedEvent.TYPE);
		Game.getInstance().getEventManager().addListener(this, MouseMovedEvent.TYPE);
	}

	@Override
	public void deinitialize() {
		Game.getInstance().getPhysicsManager().removePhysicBody(m_physicBody);
		Game.getInstance().getEventManager().removeListener(this, KeyUpEvent.TYPE);
		Game.getInstance().getEventManager().removeListener(this, KeyDownEvent.TYPE);
		Game.getInstance().getEventManager().removeListener(this, CollisionEvent.TYPE);
		Game.getInstance().getEventManager().removeListener(this, MouseButtonUpEvent.TYPE);
		Game.getInstance().getEventManager().removeListener(this, MouseButtonDownEvent.TYPE);
		Game.getInstance().getEventManager().removeListener(this, ControlChangedEvent.TYPE);
		Game.getInstance().getEventManager().removeListener(this, MouseMovedEvent.TYPE);
	}
	
	public void setPosition(double positionX, double positionY, double positionZ) {
		m_physicBody.setPosition(positionX, positionY, positionZ);
		// position for the tractor beam
	}
	
	public int getLifeCount() {
		return m_lifeCount;
	}
	
	public void update() {
		m_Steering.update();
//		limitWorldPosition();
		if (m_shooting && (m_bulletCoolDownTime + BULLET_COOL_DOWN) < m_physicBody.getTimer().getTotalElapsedTime())
			createBullet();
		if (m_collecting)
			collectRessources();
		//avoidMapObjects();
	}
	
	/*int copper =0;
	int gold = 0; 
	int stone = 0;*/
	
	
	
	private void collectRessources()
	{		
		List<Entity> resourcelist = Game.getInstance().getEntityManager().getEntitiesWithId("ResourceEntity");
		
		if(resourcelist!= null)
		{
			for (Entity e : resourcelist)
			{
				if(e instanceof ResourceEntity)
				{
					if(m_collectingCircle.isColliding(((ResourceEntity) e).getPhysicBody()) && isInView((ResourceEntity) e))
					{

						System.out.println("Collision Resource");
						((ResourceEntity) e).getPhysicBody().removeAllForces();
						double angle = Math.atan2(((ResourceEntity) e).getPositionY()-this.m_physicBody.getPositionX(), ((ResourceEntity) e).getPositionX()-this.m_physicBody.getPositionX());
						((ResourceEntity) e).getPhysicBody().setVelocity(Math.cos(angle)*10.0, Math.sin(angle)*10.0, 0.0);
					}
				}
			}			
		}
	}
	
	///calculates if the resource is in the viewing angle of the ship
	private boolean isInView(ResourceEntity e) {
		double shipPosX = m_physicBody.getPositionX();
		double shipPosY = m_physicBody.getPositionY();
		double shipPosZ = 0.0; //m_PhysicBody.getPositionZ();
		double angle = m_physicBody.getAngularPosition();
		
		double boxX1 = shipPosX-Math.cos(angle)*20.0;// + Math.sin(angle)*20.0;
		double boxX2 = shipPosX+Math.cos(angle)*20.0+Math.sin(angle)*100.0;
		double boxY1 = shipPosY-Math.sin(angle)*20.0;
		double boxY2 = shipPosY+Math.sin(angle)*20.0-Math.cos(angle)*100.0;
		
		if(boxX1 > boxX2)
		{
			double tmp = boxX1;
			boxX1 = boxX2;
			boxX2 = tmp;
		}
		if(boxY1 > boxY2)
		{
			double tmp = boxY1;
			boxY1 = boxY2;
			boxY2 = tmp;
		}
		
		if(boxX1 < e.getPositionX() && boxX2 > e.getPositionX() && boxY1 < e.getPositionY() && boxY2 > e.getPositionY())
		{
			return true;
		}
		
		//System.out.println("Ship: X: " + shipPosX + " |Y: "+shipPosY);
		//System.out.println("boxX1: " + boxX1 + " |Y1: "+boxY1 + " |X2: "+boxX2+" |Y2: "+boxY2);
		
		return false;
	}

	private void createBullet() {
		BulletEntity bullet = new BulletEntity("Bullet" + BulletEntity.bulletCount, 
				m_physicBody.getTimer().getName(),
				m_physicBody.getPositionX(), m_physicBody.getPositionY(), 0.0, m_physicBody.getAngularPosition() - Math.PI);
		Game.getInstance().getEntityManager().addEntity(bullet);

		m_bulletCoolDownTime = m_physicBody.getTimer().getTotalElapsedTime();
	}

	

	@Override
	public void onKeyDownEvent(KeyDownEvent keyDownEvent) {
		switch (keyDownEvent.getKeyCode()) {
		case 40:	//Key Down
//			m_physicBody.addForce(m_ForceDOWN);
			m_physicBody.addForce(m_ForceInAngularDirectenBackward);
			break;
		case 38:	//Key Up
//			m_physicBody.addForce(m_ForceUP);
			m_physicBody.addForce(m_ForceInAngularDirectenForward);
			break;
		case 37:	//Key Left
//			m_physicBody.addForce(m_ForceLEFT);
			m_physicBody.addForce(m_ForceRotLeft);
			break;
		case 39:	//Key Right
//			m_physicBody.addForce(m_ForceRIGHT);
			m_physicBody.addForce(m_ForceRotRight);
			break;
		case 32:	//Space Key
			m_shooting = true;
			break;
		case 88:	//X Key
			m_collecting = true;
			break;
		}
	}

	@Override
	public void onKeyUpEvent(KeyUpEvent keyUpEvent) {
		switch (keyUpEvent.getKeyCode()) {
		case 40:	//Key Down
//			m_physicBody.removeForce(m_ForceDOWN);
			m_physicBody.removeForce(m_ForceInAngularDirectenBackward);
			break;
		case 38:	//Key Up
//			m_physicBody.removeForce(m_ForceUP);
			m_physicBody.removeForce(m_ForceInAngularDirectenForward);
			break;
		case 37:	//Key Left
//			m_physicBody.removeForce(m_ForceLEFT);
			m_physicBody.removeForce(m_ForceRotLeft);
			break;
		case 39:	//Key Right
//			m_physicBody.removeForce(m_ForceRIGHT);
			m_physicBody.removeForce(m_ForceRotRight);
			break;
		case 32:	//Space Key
			m_shooting = false;
			break;
		case 88:	//X Key
			m_collecting = false;
			break;
		}
	}
	
	public void onCollisionEvent(CollisionEvent collisionEvent) {
		if(collisionEvent.isEntityInvolved(m_name)) {
			Object opponent = collisionEvent.getOpponent(m_name);
			if (opponent instanceof AsteroidEntity) {
//				ShipDestroyed();
				//Game.getInstance().getEntityManager().removeEntity(m_name);
			}
			else if(opponent instanceof ResourceEntity)
			{
				m_resources.increaseCountforType(((ResourceEntity) opponent).getType());
				Game.getInstance().getEntityManager().removeEntity((ResourceEntity)opponent);
			}
			else if(opponent instanceof Wall)
			{
				m_physicBody.setVelocity(0.8*(m_physicBody.getVelocityX()+(m_physicBody.getPositionX()-((Wall)opponent).getPositionX())), 0.8*(m_physicBody.getVelocityY()+(m_physicBody.getPositionY()-((Wall)opponent).getPositionY())), 0);
				System.out.println("Wall collision");
				m_physicBody.setVelocity(-m_physicBody.getVelocityX()/2, -m_physicBody.getVelocityY()/2, 0);
			}
		}
	}

	@Override
	public void onMouseButtonUpEvent(MouseButtonUpEvent mouseButtonUpEvent) {
		if(m_controlType == ControlType.AlternativeMouse) {
			if(mouseButtonUpEvent.getButton() == MouseEvent.BUTTON1) {
				Point m_goal = new Point((int)(mouseButtonUpEvent.getPositionX()/0.7), (int)(mouseButtonUpEvent.getPositionY()/0.7));
//				m_SteerStrategy.followPath(Game.getInstance().getPathfindingManager().calculateRoute(m_goal, new Point((int)m_physicBody.getPositionX(), (int)m_physicBody.getPositionY())));
				m_SteerStrategy.followPath(Game.getInstance().getPathfindingManager().calculateOptimizedRoute(m_goal, new Point((int)m_physicBody.getPositionX(), (int)m_physicBody.getPositionY())));
			}
		} else if(m_controlType == ControlType.MouseControl) {
			m_startedWay = false;
			System.out.println("Pfad Ende "+m_mousePoints.size());
			path.setVisual(new PathVisual(m_mousePoints));
			m_mousePoints = new ArrayList<Point2D>();
		}
	}
	

	@Override
	public void onMouseButtonDownEvent(MouseButtonDownEvent mouseButtonDownEvent) {
		if(m_controlType == ControlType.MouseControl) {
			m_startedWay = true;
			System.out.println("StartWay");
		}
	}
	
	@Override
	public void onMouseMovedEvent(MouseMovedEvent e) {
		if(m_startedWay) {
			m_mousePoints.add(new Point2D.Double(e.getNewX()/0.7, e.getNewY()/0.7));
		}
	}
	
	@Override
	public double[] getDebug() {
		double[] d = {m_physicBody.getPositionX(), m_physicBody.getPositionY()};
		return d;
	}

	@Override
	public PhysicBody getPhysicBody() {
		return m_physicBody;
	}
	
	//public CircleBody getCollectingCircleBody() {
	//	return m_collectingCircle;
	//}

//	public RectangleBody getCAvoidRectangle() {
//		return m_collisionAvoidanceRectangle;
//	}
	
	public Steering getSteering(){
		return m_Steering;
	}
	
	@Override
	public void onControlChangedEvent(ControlChangedEvent controlChangedEvent) {
		m_controlType = controlChangedEvent.getControlType();
		if(m_controlType == ControlType.MouseControl) {
			m_SteerStrategy.stop();
		}
	}

	
}
